import Vue, { CreateElement, VNode } from 'vue';

import {
  ClientAction,
  ViewFieldDescriptor,
  ListViewContext,
  ObjectViewContext,
  ColumnContext,
  CellComponentRenderer,
  TableColumn,
  TableViewConfig,
  isBoolean,
  isNumber,
  isFunction,
  omit,
  getBehaviorByKey,
  getControl,
  getRenderer,
} from '../../vendors/handie';

import { ComponentCtor } from '../../types/component';
import { isComponentCtor } from '../../utils';
import { DataTableProps } from './typing';

function createCellRenderer(
  viewContext: ObjectViewContext,
  renderFunc: (h: CreateElement) => VNode,
): ComponentCtor {
  return Vue.extend({ name: 'CellRenderer', provide: { viewContext }, render: renderFunc });
}

function resolveCellRenderer(
  field: ViewFieldDescriptor,
  context: ListViewContext,
): CellComponentRenderer<TableColumn> | undefined {
  const widget = field.widget;

  return !isFunction(widget) || isComponentCtor(widget)
    ? (h: CreateElement, data: ColumnContext<TableColumn>) =>
        h(
          createCellRenderer(context.getChildren()[data.index], () =>
            h(getRenderer('FieldRenderer'), {
              props: { field, value: data.row[data.column.key!], readonly: true },
              attrs: data,
            }),
          ),
        )
    : (widget as CellComponentRenderer<TableColumn>);
}

function isActionsAuthorized(
  actionsAuthority: string | undefined,
  authority: Record<string, boolean> | null,
): boolean {
  if (!actionsAuthority) {
    return true;
  }

  return authority ? !!authority[actionsAuthority] : false;
}

function resolveAuthorizedActions(
  actions: ClientAction[],
  actionsAuthority: string | undefined,
  authority: Record<string, boolean> | null,
): ClientAction[] {
  if (actionsAuthority) {
    return actions;
  }

  if (!authority) {
    return [];
  }

  return actions.filter(({ authority: auth }) => !auth || !!authority[auth]);
}

function resolveOperationColumn(
  context: ListViewContext,
  authority: Record<string, boolean> | null,
): TableColumn | null {
  const actionsAuthority = context.getActionsAuthority();

  const allSingleActions = resolveAuthorizedActions(
    context.getActionsByContextType('single') as ClientAction[],
    actionsAuthority,
    authority,
  );

  const col: TableColumn = {
    title: '操作',
    render: (h, { index }) => {
      const ctx = context.getChildren()[index];

      return h(
        createCellRenderer(ctx, () =>
          h(
            'div',
            resolveAuthorizedActions(
              ctx.getActions() as ClientAction[],
              actionsAuthority,
              authority,
            ).map(action => {
              const actionNode = h(getRenderer('ActionRenderer'), { props: { action } });
              const tooltipComponent = getControl('Tooltip');

              return (action.config || {}).showTooltip === true && tooltipComponent
                ? h(
                    tooltipComponent,
                    { props: { className: 'ActionWidgetTooltip', content: action.text || '' } },
                    [actionNode],
                  )
                : actionNode;
            }),
          ),
        ),
      );
    },
  };

  const { operationColumnWidth } = context.getConfig() as TableViewConfig;

  if (operationColumnWidth) {
    col.width = isNumber(operationColumnWidth)
      ? `${operationColumnWidth}px`
      : (operationColumnWidth as string);
  }

  return isActionsAuthorized(actionsAuthority, authority) && allSingleActions.length > 0
    ? col
    : null;
}

function resolveTooltipEnabled(field: ViewFieldDescriptor, context: ListViewContext): boolean {
  if (field.config && isBoolean(field.config.showOverflowTooltip)) {
    return field.config.showOverflowTooltip;
  }

  const { showTooltipWhenContentOverflow } = context.getConfig();

  return isBoolean(showTooltipWhenContentOverflow)
    ? showTooltipWhenContentOverflow
    : getBehaviorByKey('view.table.showTooltipWhenContentOverflow', false);
}

function resolveTableColumns(
  context: ListViewContext,
  authority: Record<string, boolean> | null,
): TableColumn[] {
  const cols: TableColumn[] = context.getFields().map(field => ({
    key: field.name,
    title: field.label,
    ellipsis: resolveTooltipEnabled(field, context),
    render: resolveCellRenderer(field, context),
    ...(field.config || {}),
  }));

  const {
    showSerialNumber,
    serialNumberColumnWidth = getBehaviorByKey('view.table.serialNumberColumnWidth', '55'),
    checkable,
    selectionColumnWidth = getBehaviorByKey('view.table.selectionColumnWidth', '55'),
  } = context.getConfig();

  if (showSerialNumber === true) {
    cols.unshift({ title: '序号', type: 'index', width: serialNumberColumnWidth, align: 'center' });
  }

  const actionsAuthority = context.getActionsAuthority();

  const checkableActions = isActionsAuthorized(actionsAuthority, authority)
    ? resolveAuthorizedActions(
        ([] as ClientAction[]).concat(
          (context.getActionsByContextType('batch') || []) as ClientAction[],
          (context.getActionsByContextType('both') || []) as ClientAction[],
        ),
        actionsAuthority,
        authority,
      )
    : [];

  if (isBoolean(checkable) ? checkable : checkableActions.length > 0) {
    cols.unshift({ type: 'selection', width: selectionColumnWidth, align: 'center' });
  }

  const operationCol = resolveOperationColumn(context, authority);

  if (operationCol) {
    cols.push(operationCol);
  }

  return cols;
}

function resolveTableProps(
  context: ListViewContext,
  authority: Record<string, boolean> | null,
): DataTableProps {
  return {
    ...omit(context.getConfig(), [
      'checkable',
      'showSerialNumber',
      'title',
      'showTooltipWhenContentOverflow',
      'selectionColumnWidth',
      'serialNumberColumnWidth',
      'operationColumnWidth',
    ]),
    columns: resolveTableColumns(context, authority),
  };
}

export { isActionsAuthorized, resolveAuthorizedActions, resolveTableProps };
